/**
 * 语法分析器
 * @public
 * @constructor
 * @param {String} source 源代码
 */
function SyntaxParser(source)
{
	this.init(source);
}

var $p = SyntaxParser.prototype;

/**
 * 初始化语法分析器操作
 * @protected
 * @param {String} source  源代码
 * @id SyntaxParser.prototype.init
 */
$p.init = function(source)
{
	this.source = source;
	this.depths = [];
	this.anchors = [];
	this.partitionerMap = refer(this.partitionerMap);
	this.rendererMap = refer(this.rendererMap);
	this.depVisitorMap = refer(this.depVisitorMap);
}

/**
 * 设置分区渲染函数表
 * @protected
 * @param {Object} group  源代码
 * @id SyntaxParser.prototype.setRendererMap
 */
$p.setRendererMap = function(group)
{
	this.rendererMap = {};
	
	var n, v;

	for (n in group)
	{
		v = group[n = n || this.defaultType];

		if (v instanceof Function)
		{ //is Function
			this.rendererMap[n] = v;
		}
		else
		{
			if (v instanceof Array)
			{
				v = "(\\b" + v.join("\\b|\\b") + "\\b)";
			}

			this.rendererMap[n] = this.buildRenderer(new RegExp(v, "g"), "<b class='xidea-syntax-keyword xidea-syntax-$1'>$1</b>");
		}
	}
}
/**
 * 建立渲染函数
 * @protected
 * @param {Regexp} regexp  正则表达式
 * @param {String} str  源代码
 * @id SyntaxParser.prototype.buildRenderer
 */
$p.buildRenderer = function(regexp, str)
{
	if (!(regexp instanceof RegExp))
	{
		regexp = new RegExp(regexp, "gm");
	}
	return function(text)
	{
		return text.replace(regexp, str);
	};
}
//IE5 does not support $&; and replacer function

/**
 * 设置分区表
 * @protected
 * @param {Object} group  源代码
 * @id SyntaxParser.prototype.setPartitionerMap
 */
$p.setPartitionerMap = function(group)
{
	this.partitionerMap = {};

	for (var n in group)
	{
		var pr, ps = group[n];
		if (ps instanceof RegExp)//is RegExp
		{
			pr = ps;
		}
		else
		{
			if (ps instanceof Array)
			{
				ps = ps.join("|");
			}
			pr = new RegExp(ps, "m");
		}
		this.partitionerMap[n || this.defaultType] = pr;
	}
}

/**
 * 分析源代码
 * @protected
 * @id SyntaxParser.prototype.parse
 */
$p.parse = function()
{
	if (!this.lines)
	{
		this.lines = [];
		var lreg = /\r\n|\n|\r/g
			, begin = 0
			, lm;

		while (lm = lreg.exec(this.source))
		{
			this.lines.push({"begin" : begin, "end" : lm.index});

			begin = lm.index + lm[0].length;
		}

		if (begin < this.source.length)
		{
			this.lines.push({"begin" : begin, "end" : this.source.length});
		}
		this.partitions = this.buildPartitions();
	}
}

/**
 * 默认类型
 * @private
 * @type String
 * @id SyntaxParser.prototype.defaultType
 */
$p.defaultType = "code";
/**
 * 检测类型
 * @private
 * @param {String} p 名称
 * @return {String}
 * @id SyntaxParser.prototype.guessType
 */
$p.guessType = function(p)
{
	for (var n in this.partitionerMap)
	{
		if (this.partitionerMap[n].test(p))
		{
			return n;
		}
	}
	return this.defaultType;
};

/**
 * parse partitions
 * @protected
 * @id SyntaxParser.prototype.parsePartitions
 */
$p.parsePartitions = function()
{
	var exp = [];

	for (var n in this.partitionerMap)
	{
		exp.push(this.partitionerMap[n].source);
	}
	exp = new RegExp(exp.join("|"), "gm");
	var match, ps = [], s = this.source;

	while (match = exp.exec(s))
	{
		var token = match[0];
		var type = this.guessType(token);
		ps.push(new Partition(token, type, match.index, match.index + token.length));
	}
	return ps;
};

/**
 * build partitions(fill blank part and compute depths)
 * @protected
 * @id SyntaxParser.prototype.buildPartitions
 */
$p.buildPartitions = function()
{
	var nps = [], pos = 0, dep = 0, ps = this.parsePartitions();

	for (var i = 0; i < ps.length; i++)
	{
		var partition = ps[i];

		if (partition.begin > pos)
		{
			var bp = new Partition(this.source.substring(pos, partition.begin), this.defaultType, pos, partition.begin);
			dep = this.computeDepth(bp, dep);
			nps.push(bp);
		}
		dep = this.computeDepth(partition, dep);
		nps.push(partition);
		pos = partition.end;
	}

	if (pos < this.source.length)
	{
		var partition = new Partition(this.source.substr(pos), this.defaultType, pos, this.source.length);
		dep = this.computeDepth(partition, dep);
		nps.push(partition);
	}
	return nps;
}

/**
 * compute depths.
 * default implements is scan the code for '{'|'}',if '{' <b>++depth</b> else <b>depth--</b>
 * @protected
 * @id SyntaxParser.prototype.computeDepth
 * @param {Object} partition
 * @param {Number} dep
 */
$p.computeDepth = function(partition, dep)
{
	switch (partition.type)
	{
		case "code":
			var reg = new RegExp("\{|\}", "g");
			var match, code = partition.value;

			while (match = reg.exec(code))
			{
				if (match[0] == "{")
				{
					this.depths.push(
					{
						position: partition.begin + match.index,
						preDepth: dep,
						minDepth: dep++,
						nextDepth: dep,
						type: "open"
					});
				}
				else
				{
					this.depths.push(
					{
						position: partition.begin + match.index,
						preDepth: dep,
						minDepth: -- dep,
						nextDepth: dep,
						type: "close"
					});
				}
			}

			break;

		case "muti-comment":
		case "document":
			this.depths.push(
			{
				position: partition.begin,
				preDepth: dep,
				minDepth: dep++,
				nextDepth: dep,
				type: "open"
			});

			this.depths.push(
			{
				position: partition.end - 1,
				preDepth: dep,
				minDepth: -- dep,
				nextDepth: dep,
				type: "close"
			});

			break;
	}
	return dep;
};
/**
 * build LineIterator.
 * @protected
 * @id SyntaxParser.prototype.buildLineIterator
 */
$p.buildLineIterator = function()
{
	if (this.partitions.length == 0)
	{
		this.parse();
	}
	return new LineIterator(this);
};

/**
 * 分区表
 * @param {Object} value
 * @param {String} type
 * @param {Number} begin
 * @param {Number} end
 */
function Partition(value, type, begin, end)
{
	this.value = value;
	this.type = type;
	this.begin = begin;
	this.end = end;
}

Partition.prototype.toString = function()
{
	return "{value:" + this.value + ",type:" + this.type + ",begin:" + this.begin + ",end:" + this.end + "}\n";
}

/**
 * 迭代器
 * @protected
 * @constructor
 * @param {String} sourceParser
 */
function LineIterator(sourceParser)
{
	this.parser = sourceParser;
	this.source = sourceParser.source;
	this.partitions = sourceParser.partitions;
	this.lines = sourceParser.lines;
	this.depths = sourceParser.depths;
	this.anchors = sourceParser.anchors;
	this.depthStack = [- 1];
}

$p = LineIterator.prototype;
/**
 * 层次
 * @private
 * @type Number
 * @id LineIterator.prototype.depth
 */
$p.depth = 0;

$p.nextDepth = 0;
/**
 * 起始层次
 * @private
 * @type Number
 * @id LineIterator.prototype.depthStart
 */
$p.depthStart = - 1;
/**
 * 分区索引
 * @private
 * @type Number
 * @id LineIterator.prototype.partitionIndex
 */
$p.partitionIndex = 0;
/**
 * 行索引
 * @private
 * @type Number
 * @id LineIterator.prototype.lineIndex
 */
$p.lineIndex = 0;
/**
 * 层次索引
 * @private
 * @type Number
 * @id LineIterator.prototype.depthIndex
 */
$p.depthIndex = 0;
/**
 * 锚点索引
 * @private
 * @type Number
 * @id LineIterator.prototype.anchorsIndex
 */
$p.anchorsIndex = 0;

$p.hasNext = function()
{
	return (this.partitions[this.partitionIndex] && this.lines[this.lineIndex]);
};
$p.next = function()
{
	var partition = this.partitions[this.partitionIndex];
	var l = this.lines[this.lineIndex];
	if(!partition || !l)
	{
  		return null;
	}
	try
	{
		//dell with depths
		var d = this.depths[this.depthIndex];
		if(d != null)
		{
			if(d.position<l.end)
			{
				this.depth = d.minDepth;
				while(d = this.depths[++this.depthIndex])
				{
					if(d.position<l.end)
					{
						this.depth = Math.min(this.depth,d.minDepth);
					}
					else
					{
						break;
					}
				}
				if(d)
				{
					this.nextDepth = d.preDepth;
				}
				else
				{
					this.nextDepth = 0;
					this.depth = 0;
					this.depthStack.length = 0;
					this.depthStart = -2;
				}
			}
			else
			{
				this.nextDepth = this.depth = d.preDepth;
			}
		}
		//dell with depth stack
		var i = this.depth - this.depthStack.length+1;
		if(i>0)
		{
			while(i-->0)
			{
				this.depthStack.push(this.lineIndex);
				this.depthStart = this.lineIndex;
			}
		}
		else if(i<0)
		{
			this.depthStack.length = this.depth+1;
			this.depthStart = this.depthStack[this.depth];
		}
		//deal with anchors
		var a = this.anchors[this.anchorsIndex];
		this.anchor = "";
		if(a && a.position<l.end)
		{
			this.anchor = "";
			do
			{
				this.anchor += "<a name='"+a.name+"' />";
				a = this.anchors[++this.anchorsIndex];
			}
			while(a && a.position<l.end);
		}
		//deal with line;
		if(partition.end >= l.end)
		{
			return this.render(l.begin,l.end,partition.type);
		}
		else
		{
			var buf = [];
			var i = l.begin;
			while(partition.end<l.end)
			{
				buf.push(this.render(i,i=partition.end,partition.type));
				partition = this.partitions[++this.partitionIndex];
			}
			buf.push(this.render(i,l.end,partition.type));
			return buf.join("");
		}
	}
	finally
	{
		this.lineIndex++; 
	}
}

$p.render = function(b, e, type)
{
	if (e > b)
	{
		var text = this.encodeText(this.source.substring(b, e))
			, rdr = this.parser.rendererMap[type];

		if (rdr)
		{
			text = rdr.call(this.parser, text);
		//text.replace(/@([a-zA-Z\-]+)/,this.tagReplacer);
		}
		return "<span class='xidea-syntax-" + type + "'>" + text + "</span>";
	}

	return "";
};

$p.encodeText = function(str)
{
	if(str)
	{
		return str.replace(/[\r\n]/g,"").replace(/&/g,"&amp;")
			.replace(/>/g,"&gt;").replace(/</g,"&lt;");
	}
	return str;
}

function ECMAParser(source)
{
  this.init(source);
}

$p = (Type.extend(ECMAParser, SyntaxParser)).prototype;
/**
 * 源文件
 * @protected
 * @type String
 * @id ECMAParser.prototype.source
 */
/**
 * renderer map
 * default is for javascript 
 * @protected
 * @type Object
 * @id ECMAParser.prototype.rendererMap
 */
$p.setRendererMap(
{
	"code" : ["abstract", "boolean", "break", "byte", "case", "catch", "char", "class", "const", "continue", "debugger", "default", "delete", "do", "double", "else", "enum", "export", "extends", "false", "final", "finally", "float", "for", "function", "goto", "if", "implements", "import", "in", "instanceof", "int", "interface", "long", "native", "new", "null", "package", "private", "protected", "prototype", "public", "return", "short", "static", "super", "switch", "synchronized", "this", "throw", "throws", "transient", "true", "try", "typeof", "var", "void", "volatile", "while", "with"]
	,"document": $p.buildRenderer(/@([\w-_\.\d]+)/g, "<b class='xidea-syntax-tag xidea-syntax-$1'>@$1</b>")
});

/**
 * partitions Regexp.
 * default is for javascript 
 * @protected
 * @type Object
 * @id ECMAParser.prototype.partitionerMap
 */
$p.setPartitionerMap(
{
	"document" : 		"/\\*\\*(?:[^\\*]|\\*[^/])*\\*/"
	, "muti-comment" : 	"/\\*(?:[^\\*]|\\*[^/])*\\*/"//muti-comment
	, "comment" : 		"//.*$"                    //single-comment
	, "regexp" : 		"/(?:\\\\.|[^/\\n\\r])+/"  //regexp
	, "string" : 		'"(?:\\\\(?:.|\\r|\\n|\\r\\n)|[^"\\n\\r])*"|' + "'(?:\\\\(?:.|\\r|\\n|\\r\\n)|[^'\\n\\r])*'"  //string
	, "preprocessor" : 	"^\\s*#.*"             //processor
}
);

/**
 * 针对JavaScript优化
 * guess the type of given partition.
 * @protected
 * @return {String}
 * @id ECMAParser.prototype.guessType
 */
$p.guessType = function(partition){
  var type = "";
  switch(partition.charAt(0))
  {
	case "/":
	  var c = partition.charAt(1);
	  if(c == "/")
	  {
		type = "comment";
	  }
	  else if(c == "*")
	  {
	  	type = partition.charAt(2) == "*" && partition.charAt(3) != "/" ? "document" : "muti-comment";
	  }
	  else
	  {
		type = "regexp";
	  }
	  break;
	case "'":
	case '"':
	  type = "string";
	  break;
	case " ":
	case "#":
	  type = "preprocessor";
	  break;
  }
  return type;
};

$p = null;
/*
 * copy prototypes
 */
function refer(src)
{
	var dest = {}, n;

	for (n in src)
	{
		dest[n] = src[n];
	}
	return dest;
}